import {
  callOrReturn, getExtensionField, mergeAttributes, Node, nodePasteRule
} from '@tiptap/core'
import { TextSelection } from '@tiptap/pm/state'

import {
  addColumnAfter,
  addColumnBefore,
  addRowAfter,
  addRowBefore,
  CellSelection,
  columnResizing,
  deleteColumn,
  deleteRow,
  deleteTable,
  fixTables,
  goToNextCell,
  mergeCells,
  setCellAttr,
  splitCell,
  tableEditing,
  toggleHeader,
  toggleHeaderCell
} from '@tiptap/pm/tables'

import { TableView } from './TableView.js'
import { createTable } from './utilities/createTable.js'
import { deleteTableWhenAllCellsSelected } from './utilities/deleteTableWhenAllCellsSelected.js'

const ALIGN = {
  LEFT: 'margin-left:0;margin-right: auto',
  CENTER: 'margin-left:auto;margin-right: auto',
  RIGHT: 'margin-left:auto;margin-right: 0'
}

export const Table = Node.create({
  name: 'table',

  // @ts-ignore
  addOptions() {
    return {
      HTMLAttributes: {},
      resizable: false,
      handleWidth: 5,
      cellMinWidth: 25,
      View: TableView,
      lastColumnResizable: true,
      allowTableNodeSelection: false,
    }
  },

  content: 'tableRow+',

  tableRole: 'table',

  isolating: true,

  group: 'block',

  addAttributes() {
    return {
      // styleDefault: {
      style: {
        default: 'border: none;border-collapse: collapse;',
        parseHTML: element => {
          let styles = element.getAttribute('style')?.split(';').filter(e => {
            return e && !/^[\\\n]?mso/.test(e)
          }) || ['border: none', 'border-collapse: collapse']
          return styles.join(';')
        },
        renderHTML: attr => {
          if (!attr.style) {
            return {}
          }
          return {
            style: attr.style
          }
        }
      },
      'data-border': {
        default: 'none',
        parseHTML: element => {

        }
      },
      'data-align': {
        default: 'left',
        parseHTML: element => {
          const alignment = element.getAttribute('data-align')
          const tableParentEle = element.parentElement
          let align = 'left'
          if (tableParentEle.align) {
            align = tableParentEle.align
          }
          return alignment || align
        },
        renderHTML: attr => {
          if (!attr['data-align']) {
            return {}
          }
          return {
            'data-align': attr['data-align']
          }
        }
      }
    }
  },

  parseHTML(e) {
    return [{ tag: 'table' }]
  },

  renderHTML({ HTMLAttributes }) {
    return ['table',mergeAttributes(this.options.HTMLAttributes,HTMLAttributes), ['tbody', 0]]
  },

  addCommands() {
    return {
      /**
       * 设置表格对齐方式
       * @param { 'left' | 'center' | 'right' } align default 'left'
       * @return {(function(*))|*}
       */
      setTableAlign:
        (align) => (arg) => {
        const { tr, commands, state, dispatch } = arg
          if (dispatch) {
            tr.selection.ranges.forEach(range => {
              const from = range.$from.pos
              const to = range.$to.pos
              state.doc.nodesBetween(from, to, (node, pos) => {
                  if (node.type.name === 'table') {
                    const attrs = {
                      ...node.attrs,
                      ...{
                        'data-align': align
                      },
                    }
                    tr.setNodeMarkup(pos, undefined, attrs)
                  }
              })
            })
        }
      },
      getColspanOrRowspan: () => ({ state, dispatch }) => {
        console.log(state)
      },
      insertTable:
        ({ rows = 3, cols = 3, withHeaderRow = true } = {}) => (arg) => {
          const { tr, dispatch, editor, commands } = arg
          const node = createTable(editor.schema, rows, cols, withHeaderRow)

          if (dispatch) {
            const offset = tr.selection.anchor + 1

            tr.replaceSelectionWith(node)
              .scrollIntoView()
              .setSelection(TextSelection.near(tr.doc.resolve(offset)))
          }

          return true
        },
      addColumnBefore:
        () => ({ state, dispatch }) => {
          return addColumnBefore(state, dispatch)
        },
      addColumnAfter:
        () => ({ state, dispatch }) => {
          return addColumnAfter(state, dispatch)
        },
      deleteColumn:
        () => ({ state, dispatch }) => {
          return deleteColumn(state, dispatch)
        },
      addRowBefore:
        () => ({ state, dispatch }) => {
          return addRowBefore(state, dispatch)
        },
      addRowAfter:
        () => ({ state, dispatch }) => {
          return addRowAfter(state, dispatch)
        },
      deleteRow:
        () => ({ state, dispatch }) => {
          return deleteRow(state, dispatch)
        },
      deleteTable:
        () => ({ state, dispatch }) => {
          return deleteTable(state, dispatch)
        },
      mergeCells:
        () => ({ state, dispatch }) => {
          return mergeCells(state, dispatch)
        },
      splitCell:
        () => ({ state, dispatch }) => {
          return splitCell(state, dispatch)
        },
      toggleHeaderColumn:
        () => ({ state, dispatch }) => {
          return toggleHeader('column')(state, dispatch)
        },
      toggleHeaderRow:
        () => ({ state, dispatch }) => {
          return toggleHeader('row')(state, dispatch)
        },
      toggleHeaderCell:
        () => ({ state, dispatch }) => {
          return toggleHeaderCell(state, dispatch)
        },
      mergeOrSplit:
        () => ({ state, dispatch }) => {
          if (mergeCells(state, dispatch)) {
            return true
          }

          return splitCell(state, dispatch)
        },
      setCellAttribute:
        (name, value) => ({ state, dispatch }) => {
          return setCellAttr(name, value)(state, dispatch)
        },
      goToNextCell:
        () => ({ state, dispatch }) => {
          return goToNextCell(1)(state, dispatch)
        },
      goToPreviousCell:
        () => ({ state, dispatch }) => {
          return goToNextCell(-1)(state, dispatch)
        },
      fixTables:
        () => ({ state, dispatch }) => {
          if (dispatch) {
            fixTables(state)
          }

          return true
        },
      setCellSelection:
        position => ({ tr, dispatch }) => {
          if (dispatch) {
            const selection = CellSelection.create(tr.doc, position.anchorCell, position.headCell)

            // @ts-ignore
            tr.setSelection(selection)
          }

          return true
        },
    }
  },

  addKeyboardShortcuts() {
    return {
      Tab: () => {
        if (this.editor.commands.goToNextCell()) {
          return true
        }

        if (!this.editor.can().addRowAfter()) {
          return false
        }

        return this.editor.chain().addRowAfter().goToNextCell().run()
      },
      'Shift-Tab': () => this.editor.commands.goToPreviousCell(),
      Backspace: deleteTableWhenAllCellsSelected,
      'Mod-Backspace': deleteTableWhenAllCellsSelected,
      Delete: deleteTableWhenAllCellsSelected,
      'Mod-Delete': deleteTableWhenAllCellsSelected,
    }
  },

  addProseMirrorPlugins() {
    const isResizable = this.options.resizable && this.editor.isEditable
    return [
      ...(isResizable
        ? [
          columnResizing({
            handleWidth: this.options.handleWidth,
            cellMinWidth: this.options.cellMinWidth,
            // @ts-ignore (incorrect type)
            View: this.options.View,
            // TODO: PR for @types/prosemirror-tables
            // @ts-ignore (incorrect type)
            lastColumnResizable: this.options.lastColumnResizable,
          }),
        ]
        : []),
      tableEditing({
        allowTableNodeSelection: this.options.allowTableNodeSelection,
      }),
    ]
  },

  addPasteRules() {
    return [
      nodePasteRule({
        find: text => {
          return []
        },
        type: 'table',
        getAttributes: match => {
          console.log(match)
          return {}
        }
      })
    ]
  },

  extendNodeSchema(extension) {
    const context = {
      name: extension.name,
      options: extension.options,
      storage: extension.storage,
    }
    return {
      tableRole: callOrReturn(getExtensionField(extension, 'tableRole', context)),
    }
  },
})
